/*
 * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */
package javax.swing.plaf.basic;

import java.beans.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.InputEvent;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.text.*;
import javax.swing.plaf.*;

/**
 * Provides the look and feel for a plain text editor.  In this
 * implementation the default UI is extended to act as a simple
 * view factory.
 * <p>
 * <strong>Warning:</strong>
 * Serialized objects of this class will not be compatible with
 * future Swing releases. The current serialization support is
 * appropriate for short term storage or RMI between applications running
 * the same version of Swing.  As of 1.4, support for long term storage
 * of all JavaBeans&trade;
 * has been added to the <code>java.beans</code> package.
 * Please see {@link java.beans.XMLEncoder}.
 *
 * @author Timothy Prinzing
 */
public class BasicTextAreaUI extends BasicTextUI {

  /**
   * Creates a UI for a JTextArea.
   *
   * @param ta a text area
   * @return the UI
   */
  public static ComponentUI createUI(JComponent ta) {
    return new BasicTextAreaUI();
  }

  /**
   * Constructs a new BasicTextAreaUI object.
   */
  public BasicTextAreaUI() {
    super();
  }

  /**
   * Fetches the name used as a key to look up properties through the
   * UIManager.  This is used as a prefix to all the standard
   * text properties.
   *
   * @return the name ("TextArea")
   */
  protected String getPropertyPrefix() {
    return "TextArea";
  }

  protected void installDefaults() {
    super.installDefaults();
    //the fix for 4785160 is undone
  }

  /**
   * This method gets called when a bound property is changed
   * on the associated JTextComponent.  This is a hook
   * which UI implementations may change to reflect how the
   * UI displays bound properties of JTextComponent subclasses.
   * This is implemented to rebuild the View when the
   * <em>WrapLine</em> or the <em>WrapStyleWord</em> property changes.
   *
   * @param evt the property change event
   */
  protected void propertyChange(PropertyChangeEvent evt) {
    super.propertyChange(evt);
    if (evt.getPropertyName().equals("lineWrap") ||
        evt.getPropertyName().equals("wrapStyleWord") ||
        evt.getPropertyName().equals("tabSize")) {
      // rebuild the view
      modelChanged();
    } else if ("editable".equals(evt.getPropertyName())) {
      updateFocusTraversalKeys();
    }
  }


  /**
   * The method is overridden to take into account caret width.
   *
   * @param c the editor component
   * @return the preferred size
   * @throws IllegalArgumentException if invalid value is passed
   * @since 1.5
   */
  public Dimension getPreferredSize(JComponent c) {
    return super.getPreferredSize(c);
    //the fix for 4785160 is undone
  }

  /**
   * The method is overridden to take into account caret width.
   *
   * @param c the editor component
   * @return the minimum size
   * @throws IllegalArgumentException if invalid value is passed
   * @since 1.5
   */
  public Dimension getMinimumSize(JComponent c) {
    return super.getMinimumSize(c);
    //the fix for 4785160 is undone
  }

  /**
   * Creates the view for an element.  Returns a WrappedPlainView or
   * PlainView.
   *
   * @param elem the element
   * @return the view
   */
  public View create(Element elem) {
    Document doc = elem.getDocument();
    Object i18nFlag = doc.getProperty("i18n"/*AbstractDocument.I18NProperty*/);
    if ((i18nFlag != null) && i18nFlag.equals(Boolean.TRUE)) {
      // build a view that support bidi
      return createI18N(elem);
    } else {
      JTextComponent c = getComponent();
      if (c instanceof JTextArea) {
        JTextArea area = (JTextArea) c;
        View v;
        if (area.getLineWrap()) {
          v = new WrappedPlainView(elem, area.getWrapStyleWord());
        } else {
          v = new PlainView(elem);
        }
        return v;
      }
    }
    return null;
  }

  View createI18N(Element elem) {
    String kind = elem.getName();
    if (kind != null) {
      if (kind.equals(AbstractDocument.ContentElementName)) {
        return new PlainParagraph(elem);
      } else if (kind.equals(AbstractDocument.ParagraphElementName)) {
        return new BoxView(elem, View.Y_AXIS);
      }
    }
    return null;
  }

  /**
   * Returns the baseline.
   *
   * @throws NullPointerException {@inheritDoc}
   * @throws IllegalArgumentException {@inheritDoc}
   * @see javax.swing.JComponent#getBaseline(int, int)
   * @since 1.6
   */
  public int getBaseline(JComponent c, int width, int height) {
    super.getBaseline(c, width, height);
    Object i18nFlag = ((JTextComponent) c).getDocument().
        getProperty("i18n");
    Insets insets = c.getInsets();
    if (Boolean.TRUE.equals(i18nFlag)) {
      View rootView = getRootView((JTextComponent) c);
      if (rootView.getViewCount() > 0) {
        height = height - insets.top - insets.bottom;
        int baseline = insets.top;
        int fieldBaseline = BasicHTML.getBaseline(
            rootView.getView(0), width - insets.left -
                insets.right, height);
        if (fieldBaseline < 0) {
          return -1;
        }
        return baseline + fieldBaseline;
      }
      return -1;
    }
    FontMetrics fm = c.getFontMetrics(c.getFont());
    return insets.top + fm.getAscent();
  }

  /**
   * Returns an enum indicating how the baseline of the component
   * changes as the size changes.
   *
   * @throws NullPointerException {@inheritDoc}
   * @see javax.swing.JComponent#getBaseline(int, int)
   * @since 1.6
   */
  public Component.BaselineResizeBehavior getBaselineResizeBehavior(
      JComponent c) {
    super.getBaselineResizeBehavior(c);
    return Component.BaselineResizeBehavior.CONSTANT_ASCENT;
  }


  /**
   * Paragraph for representing plain-text lines that support
   * bidirectional text.
   */
  static class PlainParagraph extends ParagraphView {

    PlainParagraph(Element elem) {
      super(elem);
      layoutPool = new LogicalView(elem);
      layoutPool.setParent(this);
    }

    public void setParent(View parent) {
      super.setParent(parent);
      if (parent != null) {
        setPropertiesFromAttributes();
      }
    }

    protected void setPropertiesFromAttributes() {
      Component c = getContainer();
      if ((c != null) && (!c.getComponentOrientation().isLeftToRight())) {
        setJustification(StyleConstants.ALIGN_RIGHT);
      } else {
        setJustification(StyleConstants.ALIGN_LEFT);
      }
    }

    /**
     * Fetch the constraining span to flow against for
     * the given child index.
     */
    public int getFlowSpan(int index) {
      Component c = getContainer();
      if (c instanceof JTextArea) {
        JTextArea area = (JTextArea) c;
        if (!area.getLineWrap()) {
          // no limit if unwrapped
          return Integer.MAX_VALUE;
        }
      }
      return super.getFlowSpan(index);
    }

    protected SizeRequirements calculateMinorAxisRequirements(int axis,
        SizeRequirements r) {
      SizeRequirements req = super.calculateMinorAxisRequirements(axis, r);
      Component c = getContainer();
      if (c instanceof JTextArea) {
        JTextArea area = (JTextArea) c;
        if (!area.getLineWrap()) {
          // min is pref if unwrapped
          req.minimum = req.preferred;
        } else {
          req.minimum = 0;
          req.preferred = getWidth();
          if (req.preferred == Integer.MAX_VALUE) {
            // We have been initially set to MAX_VALUE, but we
            // don't want this as our preferred.
            req.preferred = 100;
          }
        }
      }
      return req;
    }

    /**
     * Sets the size of the view.  If the size has changed, layout
     * is redone.  The size is the full size of the view including
     * the inset areas.
     *
     * @param width the width >= 0
     * @param height the height >= 0
     */
    public void setSize(float width, float height) {
      if ((int) width != getWidth()) {
        preferenceChanged(null, true, true);
      }
      super.setSize(width, height);
    }

    /**
     * This class can be used to represent a logical view for
     * a flow.  It keeps the children updated to reflect the state
     * of the model, gives the logical child views access to the
     * view hierarchy, and calculates a preferred span.  It doesn't
     * do any rendering, layout, or model/view translation.
     */
    static class LogicalView extends CompositeView {

      LogicalView(Element elem) {
        super(elem);
      }

      protected int getViewIndexAtPosition(int pos) {
        Element elem = getElement();
        if (elem.getElementCount() > 0) {
          return elem.getElementIndex(pos);
        }
        return 0;
      }

      protected boolean updateChildren(DocumentEvent.ElementChange ec,
          DocumentEvent e, ViewFactory f) {
        return false;
      }

      protected void loadChildren(ViewFactory f) {
        Element elem = getElement();
        if (elem.getElementCount() > 0) {
          super.loadChildren(f);
        } else {
          View v = new GlyphView(elem);
          append(v);
        }
      }

      public float getPreferredSpan(int axis) {
        if (getViewCount() != 1) {
          throw new Error("One child view is assumed.");
        }

        View v = getView(0);
        return v.getPreferredSpan(axis);
      }

      /**
       * Forward the DocumentEvent to the given child view.  This
       * is implemented to reparent the child to the logical view
       * (the children may have been parented by a row in the flow
       * if they fit without breaking) and then execute the superclass
       * behavior.
       *
       * @param v the child view to forward the event to.
       * @param e the change information from the associated document
       * @param a the current allocation of the view
       * @param f the factory to use to rebuild if the view has children
       * @see #forwardUpdate
       * @since 1.3
       */
      protected void forwardUpdateToView(View v, DocumentEvent e,
          Shape a, ViewFactory f) {
        v.setParent(this);
        super.forwardUpdateToView(v, e, a, f);
      }

      // The following methods don't do anything useful, they
      // simply keep the class from being abstract.

      public void paint(Graphics g, Shape allocation) {
      }

      protected boolean isBefore(int x, int y, Rectangle alloc) {
        return false;
      }

      protected boolean isAfter(int x, int y, Rectangle alloc) {
        return false;
      }

      protected View getViewAtPoint(int x, int y, Rectangle alloc) {
        return null;
      }

      protected void childAllocation(int index, Rectangle a) {
      }
    }
  }

}
